using Godot;

namespace GodotThirdGame.addons.MyJoyStick.scripts;

public partial class VirtualJoystick : Control
{
    [Export] public TextureRect Background;
    [Export] public TextureRect Handle;
    [Export] public float SmoothSpeed = 5.0f;

    [ExportGroup("Input Actions")]
    [Export] public string ActionLeft = "MoveLeft";
    [Export] public string ActionRight = "MoveRight";
    [Export] public string ActionUp = "MoveUp";
    [Export] public string ActionDown = "MoveDown";

    public Vector2 Dir;
    private bool _isPressed;
    private bool _pressReleased;
    private int _touchFinger;

    public override void _Process(double delta)
    {
        if (_isPressed)
        {
            _pressReleased = false;
            MoveKnobPos();
            UpdateDir();
        }
        else
        {
            ResetKnobPos();
            Dir = Vector2.Zero;
            if (!_pressReleased)
            {
                _pressReleased = true;
                Input.ActionRelease(ActionLeft);
                Input.ActionRelease(ActionRight);
                Input.ActionRelease(ActionUp);
                Input.ActionRelease(ActionDown);
            }
        }
    }

    public override void _Input(InputEvent @event)
    {
        if (@event is InputEventScreenTouch touchEvent)
        {
            bool isTouching = touchEvent.Pressed && GetGlobalRect().HasPoint(touchEvent.Position);
            if (isTouching)
            {
                _touchFinger = touchEvent.Index;
                OnTouched(touchEvent);
            }
            else if (touchEvent.Index == _touchFinger)
            {
                OnTouchReleased(touchEvent);
            }
        }
        else if (@event is InputEventMouseButton mouseEvent)
        {
            if (mouseEvent.ButtonIndex == MouseButton.Left)
            {
                if (mouseEvent.Pressed && GetGlobalRect().HasPoint(mouseEvent.Position))
                    OnTouched(mouseEvent);
                else if (!mouseEvent.Pressed && _isPressed)
                    OnTouchReleased(mouseEvent);
            }
        }
    }

    private void OnTouched(InputEvent @event) => _isPressed = true;
    private void OnTouchReleased(InputEvent @event) => _isPressed = false;

    private void MoveKnobPos()
    {
        if (GetDistance() <= GetCurrentMaxLength())
            Handle.Position = GetLocalTouchPos();
        else
        {
            float angle = GetBackCenterPos().AngleToPoint(GetGlobalMousePosition());
            Handle.Position = new Vector2(
                (GetBackCenterPos().X + Mathf.Cos(angle) * GetCurrentMaxLength()) - GetCenterPos().X,
                (GetBackCenterPos().Y + Mathf.Sin(angle) * GetCurrentMaxLength()) - GetCenterPos().Y
            );
        }
    }

    private void ResetKnobPos() => Handle.Position = Handle.Position.Lerp(Vector2.Zero + Size / 2 - Handle.Size / 2, SmoothSpeed * (float)GetProcessDeltaTime());

    private Vector2 GetBackCenterPos() => Position + Size / 2;
    private Vector2 GetCenterPos() => Position + Handle.Size / 2;
    private Vector2 GetLocalTouchPos() => (GetGlobalMousePosition() - GetCenterPos()) / Scale.X;
    private float GetDistance() => GetGlobalMousePosition().DistanceTo(GetBackCenterPos()) / Scale.X;
    private float GetCurrentMaxLength() => Background.Texture.GetSize().X / 2;

    private Vector2 _lastDir;
    private void UpdateDir()
    {
        Dir = Handle.Position + Handle.Size / 2;
        Dir /= Size / 2;
        Dir -= Vector2.One;
        
        if (Dir.X != 0)
        {
            if (Dir.X > 0)
            {
                Input.ActionPress(ActionRight, Dir.X);
                Input.ActionRelease(ActionLeft);
            }
            else
            {
                Input.ActionPress(ActionLeft, -Dir.X);
                Input.ActionRelease(ActionRight);
            }
        }
        else
        {
            Input.ActionRelease(ActionLeft);
            Input.ActionRelease(ActionRight);
        }
        
        if (Dir.Y != 0)
        {
            if (Dir.Y < 0)
            {
                Input.ActionPress(ActionUp, -Dir.Y);
                Input.ActionRelease(ActionDown);
            }
            else
            {
                Input.ActionPress(ActionDown, Dir.Y);
                Input.ActionRelease(ActionUp);
            }
        }
        else
        {
            Input.ActionRelease(ActionUp);
            Input.ActionRelease(ActionDown);
        }
    }
}